//
//  DPTestsRunner.m
//  UITestingKit
//
//  Created by Ofri Wolfus on 26/02/07.
//  Copyright 2007 Ofri Wolfus. All rights reserved.
//

#import "DPTestsRunner.h"
#import <UITestingKit/UITestingKit.h>
#import <UITestingKit/DPAsyncTestsRunner.h>
#import <UITestingKit/DPUITester.h>


#define DPWORKING_CODE	-9387

@implementation DPTestsRunner

- (id)init {
	if ((self = [super init])) {
		modules = [[NSMutableArray alloc] init];
		verbose = NO;
		
		// Set up an autorelease pool
		DPSetUpAutoreleasePool(NSDefaultRunLoopMode);
	}
	
	return self;
}

- (void)dealloc {
	[modules release]; modules = nil;
	[super dealloc];
}

- (void)loadModules:(NSArray *)paths {
	NSEnumerator *enumerator = [paths objectEnumerator];
	NSString *wd = [[NSFileManager defaultManager] currentDirectoryPath];
	NSString *path;
	NSFileManager *fm = [NSFileManager defaultManager];
	
	while ((path = [enumerator nextObject])) {
		path = [path stringByStandardizingPath];
		
		if (![path hasPrefix:@"/"])
			path = [wd stringByAppendingPathComponent:path];
		
		if ([path hasSuffix:@".testsModule"] || [path hasSuffix:@".testsModule/"]) {
			id module = [DPTestsModule moduleAtPath:path];
			if (module)
				[modules addObject:module];
		} else {
			BOOL dir;
			if ([fm fileExistsAtPath:path isDirectory:(BOOL *)&dir] && dir)
				[modules addObjectsFromArray:[DPTestsModule modulesAtDirectory:path]];
		}
	}
}

- (int)runIgnoringExceptions:(BOOL)catchExceptions
			   stopOnFailure:(BOOL)stopOnFailedTests
						 log:(NSString **)log
{
	DPAsyncTestsRunner *runner = [[DPAsyncTestsRunner alloc] initWithModules:modules];
	SInt32 rlResult = kCFRunLoopRunHandledSource;
	NSRunLoop *rl = [NSRunLoop currentRunLoop];
	
	// Set up our tests runner
	[runner setDelegate:self];
	[runner setCatchExceptions:catchExceptions];
	[runner setStopsOnFailedTests:stopOnFailedTests];
	[runner setIsVerbose:verbose];
	
	// Mark ourself as working
	returnCode = DPWORKING_CODE;
	
	// Add the runner to the current runloop
	[rl addSource:runner
		  forMode:NSDefaultRunLoopMode];
	[runner signal];
	
	// Run the current runloop once until our return code changes
	// or there are no sources to process.
	// returnCode will be changed by the callbacks of DPAsyncTestsRunner.
	while (returnCode == DPWORKING_CODE && rlResult != kCFRunLoopRunFinished)
		rlResult = CFRunLoopRunInMode((CFStringRef)NSDefaultRunLoopMode, 0.5, true);
	//while (returnCode == DPWORKING_CODE && [rl runMode:NSDefaultRunLoopMode
	//										beforeDate:[NSDate distantFuture]]);
	
	// Return the log if needed
	if (log)
		*log = [runner log];
	
	return returnCode;
}

- (void)testsRunner:(DPAsyncTestsRunner *)runner reportedFailedTest:(DPTestStatistics *)stats {
	// No need for this at this time
}

- (void)testsRunnerFinished:(DPAsyncTestsRunner *)runner withStatus:(DPTestStatus)stats {
	switch (stats) {
		case DPTestFailed:
			returnCode = -1;
			break;
		default:
			returnCode = 0;
			break;
	}
}

- (NSArray *)modules {
	return [[modules copy] autorelease];
}

- (void)setVerbose:(BOOL)flag {
	verbose = flag;
}

- (int)runUITests:(BOOL)printStackTrace {
	DPUITester *tester = [[DPUITester alloc] initWithModules:modules];
	SInt32 rlResult = kCFRunLoopRunHandledSource;
	NSRunLoop *rl = [NSRunLoop currentRunLoop];
	
	// Configure our UI tester
	[tester setPrintStackTrace:printStackTrace];
	
	// Add our UI tester to the current runloop
	[rl addSource:tester forMode:NSDefaultRunLoopMode];
	[tester signal];
	
	// Run our runloop in the default mode until it has no sources to process.
	// When our tester is done it'll invalidate itself which will remove it from
	// all runloops.
	while (rlResult != kCFRunLoopRunFinished && [rl containsSource:tester
															inMode:NSDefaultRunLoopMode])
	{
		rlResult = CFRunLoopRunInMode((CFStringRef)NSDefaultRunLoopMode, 1.0, TRUE);
	}
	
	// Clean up
	[tester autorelease];
	return [tester returnValue];
}

- (BOOL)hasUnitTests {
	NSEnumerator *e = [modules objectEnumerator];
	DPTestsModule *module;
	
	while ((module = [e nextObject]))
		if ([module casesCount] > 0U)
			return YES;
	
	return NO;
}

- (BOOL)hasUITests {
	NSEnumerator *e = [modules objectEnumerator];
	DPTestsModule *module;
	
	while ((module = [e nextObject]))
		if ([module userClass])
			return YES;
	
	return NO;
}

@end
